#include "SwarmSceneResources.h"

#include <cassert>
#include <d3d11.h>
#include <DirectXMath.h>

#include "main/Globals.h"

#include "managers/ShaderResourcesManager.h"

#include "scenes/swarmScene/SwarmSceneSettings.h"

#include "utils/MathUtils.h"

using namespace DirectX;

namespace swarmScene {
	Resources::Resources(const Settings& settings)
		: mQuadTextureSRV(nullptr)
		, mEntitiesPosW(nullptr)
		, mEntitiesPosWSRV(nullptr)
		, mEntitiesPosWUAV(nullptr)
		, mEntitiesVelW(nullptr)
		, mEntitiesVelWUAV(nullptr)
	{
		// Texture used for entities
		assert(Globals::gResources);
		mQuadTextureSRV = Globals::gResources->addTextureFromFileSRV(L"particle", L"tests/particle.dds");
		assert(mQuadTextureSRV);

		// Entities buffer
		buildEntitiesBuffer(settings.mNumEntities);

		//
		// Vertex shader resources
		//

		// Entities position buffer SRV
		{
			const uint32_t numEntities = settings.mNumEntities;
			D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
			srvDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
			srvDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER;
			srvDesc.Buffer.FirstElement = 0;
			srvDesc.Buffer.NumElements = numEntities;
			mEntitiesPosWSRV = Globals::gResources->addResourceSRV(L"entitiesPosWSRV", mEntitiesPosW, srvDesc);
			assert(mEntitiesPosWSRV);
		}


		//
		// Compute shader resources
		// 

		// Entities position buffer UAV
		{
			const uint32_t numEntities = settings.mNumEntities;
			D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc;
			uavDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
			uavDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
			uavDesc.Buffer.FirstElement = 0;
			uavDesc.Buffer.Flags = 0;
			uavDesc.Buffer.NumElements = numEntities;
			mEntitiesPosWUAV = Globals::gResources->addResourceUAV(L"entitiesPosWUAV", mEntitiesPosW, uavDesc);
			assert(mEntitiesPosWUAV);
		}
	}

	void
	Resources::buildEntitiesBuffer(const uint32_t numEntities) {
		assert(mEntitiesPosW == nullptr);
		
		//
		// Create entities position buffer
		//
		{
			// Create data to fill buffer.
			D3D11_SUBRESOURCE_DATA initData;
			std::vector<XMFLOAT4> entitiesPosW;
			entitiesPosW.reserve(numEntities);
			const float posFactor = 300.0f;
			const float originFactor = 300.0f;
			for (size_t i = 0; i < numEntities; ++i) {
				if (i % 5 == 0) {
					const float origin[3] = { originFactor, originFactor, 0.0f };
					const float posX = randomFloat(origin[0] - posFactor, origin[0] + posFactor);
					const float posY = randomFloat(origin[1] - posFactor, origin[1] + posFactor);
					const float posZ = randomFloat(origin[2] - posFactor, origin[2] + posFactor);
					const XMFLOAT4 posW(posX, posY, posZ, 1.0f);
					entitiesPosW.push_back(posW);
				}
				else if (i % 5 == 1) {
					const float origin[3] = { originFactor, -originFactor, 0.0f };
					const float posX = randomFloat(origin[0] - posFactor, origin[0] + posFactor);
					const float posY = randomFloat(origin[1] - posFactor, origin[1] + posFactor);
					const float posZ = randomFloat(origin[2] - posFactor, origin[2] + posFactor);
					const XMFLOAT4 posW(posX, posY, posZ, 1.0f);
					entitiesPosW.push_back(posW);
				}
				else if (i % 5 == 2) {
					const float origin[3] = { -originFactor, originFactor, 0.0f };
					const float posX = randomFloat(origin[0] - posFactor, origin[0] + posFactor);
					const float posY = randomFloat(origin[1] - posFactor, origin[1] + posFactor);
					const float posZ = randomFloat(origin[2] - posFactor, origin[2] + posFactor);
					const XMFLOAT4 posW(posX, posY, posZ, 1.0f);
					entitiesPosW.push_back(posW);
				}
				else if (i % 5 == 3) {
					const float origin[3] = { -originFactor, -originFactor, 0.0f };
					const float posX = randomFloat(origin[0] - posFactor, origin[0] + posFactor);
					const float posY = randomFloat(origin[1] - posFactor, origin[1] + posFactor);
					const float posZ = randomFloat(origin[2] - posFactor, origin[2] + posFactor);
					const XMFLOAT4 posW(posX, posY, posZ, 1.0f);
					entitiesPosW.push_back(posW);
				}
				else {
					const float origin[3] = { -0, -0, 0.0f };
					const float posX = randomFloat(origin[0] - posFactor, origin[0] + posFactor);
					const float posY = randomFloat(origin[1] - posFactor, origin[1] + posFactor);
					const float posZ = randomFloat(origin[2] - posFactor, origin[2] + posFactor);
					const XMFLOAT4 posW(posX, posY, posZ, 1.0f);
					entitiesPosW.push_back(posW);
				}
			}

			initData.pSysMem = &entitiesPosW[0];

			// Create buffer.
			D3D11_BUFFER_DESC bufferDesc;
			ZeroMemory(&bufferDesc, sizeof(D3D11_BUFFER_DESC));
			bufferDesc.BindFlags = D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE;
			bufferDesc.ByteWidth = sizeof(XMFLOAT4) * numEntities;
			bufferDesc.CPUAccessFlags = 0;
			bufferDesc.MiscFlags = 0;
			bufferDesc.StructureByteStride = 0;
			bufferDesc.Usage = D3D11_USAGE_DEFAULT;
			mEntitiesPosW = Globals::gResources->addBuffer(L"entitiesPosW", bufferDesc, &initData);
			assert(mEntitiesPosW);
		}
	}
}